#ifndef LISTENTHRD_HPP
#define LISTENTHRD_HPP

#include <iostream>
#include <winsock2.h>
#include<thread>
#include<unordered_map>
#include<list>
#include<chrono>
#include<vector>

#include<xsignal/signal.hpp>

struct SocketCfg{
    bool isConnected = false;   //记录socket的连接状态，当连接断开时方便服务端检测
    std::thread thrd;
};

//构造函数形参列表
struct listenThrdCfg{
    std::string ip;
    unsigned int port;
};

class XTcpServer{
public:
    XTcpServer(listenThrdCfg msg){
        WSADATA wsaData;
        WSAStartup( MAKEWORD(2, 2), &wsaData);
        
        //创建套接字
        m_servSock = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);

        //绑定套接字
        SOCKADDR_IN sockAddr;
        memset(&sockAddr, 0, sizeof(sockAddr));  //每个字节都用0填充
        sockAddr.sin_family = PF_INET;  //使用IPv4地址
        sockAddr.sin_addr.s_addr = inet_addr(msg.ip.c_str());  //具体的IP地址
        sockAddr.sin_port = htons(msg.port);  //端口
        ::bind(m_servSock, (SOCKADDR*)&sockAddr, sizeof(SOCKADDR));

        //进入监听状态
        listen(m_servSock, 20);	//20表示存放的客户端请求个数

        //设为非阻塞模式
        ULONG NonBlock = 1; 
        ioctlsocket(m_servSock, FIONBIO, &NonBlock);
        
        m_acceptThrd = std::thread(&XTcpServer::acceptThrd_exec,this);
    }

    ~XTcpServer()
    {   
        std::cout<<__FUNCTION__<<"enter. thrdId:"<<std::this_thread::get_id()<<std::endl;
        //停止m_acceptThrd内部循环
        m_isRun = false;    
        
        if(m_acceptThrd.joinable())
            m_acceptThrd.join();

        //关闭所有socket线程循环
        m_terminateAllThrd = true;

        //关闭socket、停止线程
        std::cout<<"active socket num:"<<m_socketInfo.size()<<std::endl;
        for (auto &s : m_socketInfo){
            closesocket(s.first);       //主动关闭连接无法检测到
            if(s.second.thrd.joinable()){
                s.second.isConnected = false;
                s.second.thrd.join();
            }
        }
        std::cout<<"All active socketn has been closed"<<std::endl;
            
        //关闭socket
        closesocket(m_servSock);

        // 终止 DLL 的使用
        WSACleanup();
        std::cout<<__FUNCTION__<<"exit. thrdId:"<<std::this_thread::get_id()<<std::endl;
    }

private:
    // 判断m_socketInfo，以便客户端主动断开连接时及时回收线程资源
    void deal_ClosedSocket()
    {
        std::list<SOCKET> tmp;
        for (auto &s : m_socketInfo)
        {
            if (s.second.isConnected == false)
            {
                if (s.second.thrd.joinable())
                {
                    s.second.thrd.join();
                    tmp.push_back(s.first);
                }
            }
        }

        for (auto &s : tmp)
        {
            m_socketInfo.erase(s);
        }
    }

    //非阻塞方式处理accept
    void acceptThrd_exec()
    {
        std::cout<<__FUNCTION__<<" enter. thrdId:"<<std::this_thread::get_id()<<std::endl;
        struct timeval t{0,5};
        while (m_isRun)
        {
            fd_set socketSet{};
            FD_SET(m_servSock, &socketSet);
            int res = select(0, &socketSet, nullptr, nullptr, &t);
            if(res>0)
            {
                for (int i = 0; i < socketSet.fd_count; i++)
                {
                    SOCKADDR clntAddr;
                    int nSize = sizeof(SOCKADDR);
                    SOCKET clntSock = accept(m_servSock, (SOCKADDR *)&clntAddr, &nSize);
                    std::thread thrd(&XTcpServer::newSocketThrd_exec, this, clntSock,m_reciveBufSize);
                    SocketCfg info = {true, std::move(thrd)};
                    m_socketInfo.insert({clntSock, std::move(info)});
                    xemit(sig_connect)(clntSock);
                }
            }
            
            deal_ClosedSocket();
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
        }
        std::cout<<__FUNCTION__<<" exit. thrdId:"<<std::this_thread::get_id()<<std::endl;
    }

    //为每一个新建立的socket创建单独线程维护
    void newSocketThrd_exec(SOCKET s,int MaxBufSize)
    {
         std::cout<<__FUNCTION__<<"enter. thrdId: "<<std::this_thread::get_id()<<std::endl;
        // 根据socket，获取客户端的信息
        struct sockaddr_in sa;
        int namlen = sizeof(struct sockaddr);
        int res = getpeername(s, (struct sockaddr *)&sa, &namlen);
        if (res == 0)
        {
             std::cout << "addr:" << inet_ntoa(sa.sin_addr) <<" port:"<<sa.sin_port<< std::endl;
        }
        char * buf = new char[MaxBufSize+1]{};

        while (m_socketInfo[s].isConnected && (!m_terminateAllThrd))
        {
             // 检查接口状态
             fd_set socketSet{};
             FD_SET(s, &socketSet);

             struct timeval t{};
             select(0, &socketSet, nullptr, nullptr, &t); // 非阻塞判断

             if (socketSet.fd_count)
             {
                 // 当前socket可读取，即表示有数据到达
                 res = recv(s, buf, MaxBufSize, 0);
                 if (res > 0)
                 {  
                    //新数据到达
                    xemit(sig_read)(s,buf,res);
                 }
                 if (res == 0)
                 {
                     m_socketInfo[s].isConnected = false;//结束循环
                     xemit(sig_disconnect)(s);
                 }
             }
             std::this_thread::sleep_for(std::chrono::milliseconds(200));
        }
        delete[] buf;
        std::cout<<__FUNCTION__<<"exit. thrdId: "<<std::this_thread::get_id()<<std::endl;
    }
public:
    void write(SOCKET s,char* buf,int len){
        ::send(s,buf,len,0);
    }

private:
    std::unordered_map<SOCKET,SocketCfg> m_socketInfo;  
    SOCKET m_servSock;
    bool m_isRun = true;
    bool m_terminateAllThrd = false;
    const unsigned int m_reciveBufSize = 2048;
    std::thread m_acceptThrd;

public:
    XSIGNAL(sig_read,SOCKET,char*,int);            //指定的socket数据更新
    XSIGNAL(sig_disconnect,SOCKET);                 //指定的socket关闭
    XSIGNAL(sig_connect,SOCKET);                      //指定的socket建立连接
};
#endif